package top.waikin.aspect.aspect;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import top.waikin.aspect.annonation.ClearCache;
import top.waikin.aspect.annonation.EnableCache;
import top.waikin.aspect.annonation.Key;
import top.waikin.aspect.annonation.RequireCache;
import top.waikin.aspect.component.CacheManager;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.StringJoiner;

/**
 * 缓存切面
 *
 * @author WaiKin
 * @date 2022/11/26
 */
@Slf4j
@Aspect
@Component
public class CacheAspect {

  private static final String KEY_SEPARATOR = ":";
  private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer =
      new DefaultParameterNameDiscoverer();
  @Resource CacheManager cacheManager;

  /** 拦截请求缓存的方法 */
  @Around(
      value = "@within(enableCache)&&@annotation(requireCache)",
      argNames = "pjp,enableCache,requireCache")
  public Object doAround(
      ProceedingJoinPoint pjp, EnableCache enableCache, RequireCache requireCache)
      throws Throwable {
    StringJoiner keyJoiner = new StringJoiner(KEY_SEPARATOR);
    keyJoiner.add(enableCache.keyPrefix());
    if (StrUtil.isNotEmpty(requireCache.keySuffix())) {
      keyJoiner.add(requireCache.keySuffix());
    }
    joinKey(pjp, keyJoiner);
    String key = keyJoiner.toString();

    // 从缓存中获取
    Object cache = cacheManager.get(key);
    // 缓存不为空 直接返回
    if (ObjectUtil.isNotNull(cache)) {
      return cache;
    }
    // 允许存储null 并且缓存存在key 返回null
    if (requireCache.storageNullFlag() && cacheManager.containsKey(key)) {
      return null;
    }

    Object result = pjp.proceed();

    // 结果不为null 或者允许存储null 重入缓存
    if (ObjectUtil.isNotNull(result) || requireCache.storageNullFlag()) {
      long ttl = enableCache.ttl();
      if (requireCache.ttl() != 0) {
        ttl = requireCache.ttl();
      }
      cacheManager.set(key, result, ttl);
    }
    return result;
  }

  /** 拦截清除缓存的方法 */
  @Around(
      value = "@within(enableCache)&&@annotation(clearCache)",
      argNames = "pjp,enableCache,clearCache")
  public Object doAround(ProceedingJoinPoint pjp, EnableCache enableCache, ClearCache clearCache)
      throws Throwable {
    StringJoiner keyJoiner = new StringJoiner(KEY_SEPARATOR);
    keyJoiner.add(enableCache.keyPrefix());
    if (StrUtil.isNotEmpty(clearCache.keySuffix())) {
      keyJoiner.add(clearCache.keySuffix());
    }
    joinKey(pjp, keyJoiner);
    String key = keyJoiner.toString();
    cacheManager.remove(key);
    return pjp.proceed();
  }

  /** 添加key */
  private void joinKey(ProceedingJoinPoint pjp, StringJoiner stringJoiner) {
    Object[] args = pjp.getArgs();
    MethodSignature signature = (MethodSignature) pjp.getSignature();
    Method method = signature.getMethod();
    Parameter[] parameters = method.getParameters();
    String[] parameterNames = defaultParameterNameDiscoverer.getParameterNames(method);
    // 遍历方法参数 找带有Key注解的参数
    for (int i = 0; i < parameters.length; i++) {
      Key annotation = parameters[i].getAnnotation(Key.class);
      if (ObjectUtil.isNotNull(annotation)) {
        String fieldName = annotation.fieldName();
        if (StrUtil.isEmpty(fieldName)) {
          fieldName = parameterNames[i];
        }
        // fieldName:arg
        stringJoiner.add(fieldName).add(String.valueOf(args[i]));
      }
    }
  }
}
